
package hk.ces.oxfam.patch.xml.factory;

import hk.ces.oxfam.patch.util.StringUtil;
import hk.ces.oxfam.patch.xml.SQLResultXML;
import hk.ces.oxfam.patch.xml.SimpleXML;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

/**
 *
 * @author ReutyCheng
 */
public class SQLResultXMLFactory extends AbstractXMLFactory<SQLResultXML>
{
  private static final String NODE_SQL_RESULTS = "SQLResults";
  private static final String NODE_SQL_RESULT = "SQLResult";
  private static final String NODE_METAS = "Metas";
  private static final String NODE_META = "Meta";
  private static final String NODE_COLUMNS = "Columns";
  private static final String NODE_COLUMN = "Column";
  private static final String NODE_ROWS = "Rows";
  private static final String NODE_ROW = "Row";
  private static final String NODE_OBJECT = "Object";

  private static final String ATTR_SQL = "sql";
  private static final String ATTR_PARAMS = "params";
  private static final String ATTR_CREATED_TIME = "createdTime";
  private static final String ATTR_METANAME = "metaname";
  private static final String ATTR_METAVALUE = "metavalue";

  public SQLResultXMLFactory(String fileName)
  {
    super(fileName);
  }

  public SQLResultXMLFactory(String folder, String fileName)
  {
    super(folder, fileName);
  }

  @Override
  protected String getXslPath()
  {
    return "/sql-result-style.xsl";
  }

  @Override
  protected Document getDocument()
  {
    Document document = addXMLStyleSheet(DocumentHelper.createDocument());
    Element sqlResultsElement = createSQLResults(document);
    SimpleXMLFactory factory = new SimpleXMLFactory();
    factory.resetNodeName(new String[]{NODE_COLUMNS, NODE_COLUMN, NODE_ROWS, NODE_ROW, NODE_OBJECT});

    for (SQLResultXML result : this.getXMLItems())
    {
      Element sqlResultElement = createSQLResult(sqlResultsElement, result.getSql(), result.getParams(), new Date());
      createMetas(sqlResultElement, result.getMetas());

      if (result instanceof SimpleXML)
      {
        SimpleXML simpleXML = (SimpleXML) result;
        factory.setSimpleXML(simpleXML);

        factory.createHeaders(sqlResultElement);
        factory.createBodyItems(sqlResultElement);
      }
    }

    return document;
  }

  private Document addXMLStyleSheet(Document document)
  {
    Map instructions = new HashMap();
    instructions.put("type", "text/xsl");
    instructions.put("href", "sql-result-style.xsl");

    return document.addProcessingInstruction("xml-stylesheet", instructions);
  }

  private Element createSQLResults(Document document)
  {
    return document.addElement(NODE_SQL_RESULTS);
  }

  private Element createSQLResult(Element sqlResultsElement, String sql, Object[] params, Date createdDate)
  {
    Element sqlResultElement = sqlResultsElement.addElement(NODE_SQL_RESULT);
    sqlResultElement.addAttribute(ATTR_SQL, sql);
    sqlResultElement.addAttribute(ATTR_PARAMS, StringUtil.toString(params));
    sqlResultElement.addAttribute(ATTR_CREATED_TIME, createdDate != null ? createdDate.toString() : "");

    return sqlResultElement;
  }

  private Element createMetas(Element sqlResultElement, Map<String, String> metaMap)
  {
    Element metasElement = sqlResultElement.addElement(NODE_METAS);
    for (Map.Entry<String, String> entry : metaMap.entrySet())
    {
      Element metaElement = metasElement.addElement(NODE_META);
      metaElement.addAttribute(ATTR_METANAME, entry.getKey());
      metaElement.addAttribute(ATTR_METAVALUE, entry.getValue());
    }
    
    return metasElement;
  }

  private Element createColumns(Element sqlResultElement, String[] columnNames)
  {
    Element columnsElement = sqlResultElement.addElement(NODE_COLUMNS);
    for (String columnName : columnNames)
    {
      Element columnElement = columnsElement.addElement(NODE_COLUMN);
      columnElement.setText(columnName);
    }
    return columnsElement;
  }

  private Element createRows(Element sqlResultElement, List<Object[]> objectsList)
  {
    Element rowsElement = sqlResultElement.addElement(NODE_ROWS);
    for (Object[] objects : objectsList)
    {
      Element rowElement = rowsElement.addElement(NODE_ROW);
      for (Object object : objects)
      {
        Element objectElement = rowElement.addElement(NODE_OBJECT);
        objectElement.setText(object != null ? object.toString() : "");
      }
    }
    return rowsElement;
  }
}
